home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 1.iso
/
ARGONET
/
PD
/
SOUND
/
REPLAYER.SPK
/
c
/
command
next >
Wrap
Text File
|
1998-09-04
|
15KB
|
460 lines
/* command.c
Replayer -- audio player
Copyright (c) 1997/8 Mark Seaborn <mseaborn@argonet.co.uk>
This source file provides a quick command line front end to Replayer.
It has some diagnostic features to print out the headers of a Replay
file. This is all portable, but RISC OS-specific features can be
enabled by defining `RISCOS' (enables TaskWindow idling, interactive
use).
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Dreamscape:bool.h"
#ifdef RISCOS
#include "OS:osbyte.h"
#include "OS:taskwindow.h"
#endif
#ifdef MemCheck_MEMCHECK
#include "MemCheck:MemCheck.h"
#endif
#include "Replayer:replayer.h"
/* Declarations */
#define tool "replayer"
static void usage(void);
static void print_time(int *length, const replayer_file *file);
static void remove_time(int *length);
static char *time_string(int time);
static void print_replay_header(FILE *file, const replayer_header *header);
static void print_replay_chunk_catalogue(FILE *file,
const replayer_chunk_entry *chunks, int no_chunks, int soundtracks);
/* Main functions */
/* Prints Replayer's little help message. */
static void usage(void)
{
fprintf(stderr, "Usage: %s [options] [--] filenames...\n\n"
"Where available options are:\n\n"
" -d, --diagnostics Print file header info as a diagnostic\n"
" -p, --play Plays the sound part of the Replay file\n"
" -v, --verbose Print status messages\n"
" -q, --quality n Sound quality, ranging from 1--4 (default 4)\n"
" -l, --loop Play the list of files repeatedly\n"
" -t, --timer Display timer\n"
#ifndef replayer_NO_BACKWARDS
" -b, --backwards Play sound backwards\n"
#endif
#ifdef RISCOS
" -i, --interactive Interactive mode; accept keypresses\n"
#endif
" -h, --help Show this message\n"
" filenames... The name(s) of the file(s) to play\n"
"\n", tool);
}
/* The main part of the command line tool. */
int main(int argc, char *argv[])
{
/* Some of the variables we need. */
bool play = 0, diagnostics = 0, verbose = 0, loop = 0, timer = 0;
int quality = 4;
#ifndef replayer_NO_BACKWARDS
bool backwards = 0;
#endif
#ifdef RISCOS
bool interactive = 0,
muted = 0; /* Preserve mute state between files. */
#endif
int i, /* Misc counter. */
first_file_arg = argc, /* First non-switch argument. */
next_arg; /* Next argument to use as filename. */
/* Turn MemCheck on if it's there. */
#ifdef MemCheck_MEMCHECK
MemCheck_Init();
MemCheck_RegisterArgs(argc, argv);
MemCheck_InterceptSCLStringFunctions();
MemCheck_SetStoreMallocFunctions(1);
#endif
/* Parse the arguments we're given. */
for(i=1; i<argc; ++i) {
if(argv[i][0] == '-' && argv[i][1]) {
const char *c;
/* Long options, ie. `--foo'. */
if(argv[i][1] == '-' && argv[i][2]) {
const char *arg = argv[i] + 2;
if(!strcmp(arg, "diagnostics")) { diagnostics = 1; continue; }
if(!strcmp(arg, "play")) { play = 1; continue; }
if(!strcmp(arg, "verbose")) { verbose = 1; continue; }
if(!strcmp(arg, "quality")) { quality = atoi(argv[++i]); continue; }
if(!strcmp(arg, "loop")) { loop = 1; continue; }
if(!strcmp(arg, "timer")) { timer = 1; continue; }
#ifndef replayer_NO_BACKWARDS
if(!strcmp(arg, "backwards")) { backwards = 1; continue; }
#endif
#ifdef RISCOS
if(!strcmp(arg, "interactive")) { interactive = 1; continue; }
#endif
if(!strcmp(arg, "help")) { usage(); return EXIT_SUCCESS; }
fprintf(stderr, "%s: Unknown argument, `%s'\n", tool, argv[i]);
return EXIT_FAILURE;
}
/* Short options, ie. `-f -v' or combined, `-fv'. */
if(argv[i][1] != '-') {
for(c=argv[i]+1; *c; ++c) switch(*c) {
case 'd': diagnostics = 1; continue;
case 'p': play = 1; continue;
case 'v': verbose = 1; continue;
case 'q': quality = atoi(argv[++i]); continue;
case 'l': loop = 1; continue;
case 't': timer = 1; continue;
#ifndef replayer_NO_BACKWARDS
case 'b': backwards = 1; continue;
#endif
#ifdef RISCOS
case 'i': interactive = 1; continue;
#endif
case 'h': usage(); return EXIT_SUCCESS;
default:
fprintf(stderr, "%s: Unknown argument, `-%c'\n", tool, *c);
return EXIT_FAILURE;
}
continue;
}
}
/* The rest are non-switch arguments. */
first_file_arg = i;
/* If this is the switch terminator `--', move on one. */
if(argv[i][0] == '-' && argv[i][1] == '-' && !argv[i][2])
++first_file_arg;
break;
}
if(verbose) printf("%s: Starting...\n", tool);
/* Complain if no filename was passed. */
if(first_file_arg >= argc) {
fprintf(stderr, "%s: You must supply at least one filename "
"of a Replay file.\n\n", tool);
usage();
return EXIT_FAILURE;
}
/* Check quality value is valid. */
if(quality < 1 || quality > 4) {
fprintf(stderr, "%s: Quality value %i invalid (must be 1--4)\n",
tool, quality);
return EXIT_FAILURE;
}
/* If no options were set, default to playing. */
if(!play && !diagnostics) play = 1;
#ifdef RISCOS
if(interactive) printf("%s: Interactive mode; press `h' for help\n", tool);
#endif
/* Deal with each file in turn. */
next_arg = first_file_arg;
while(next_arg < argc) {
replayer_file *file;
bool finish_loop = 0;
const char *filename = argv[next_arg++];
/* Open the Replay file. */
file = replayer_create_file(filename);
if(!file) {
fprintf(stderr, "%s: Couldn't open Replay file `%s'\n",
tool, filename);
return EXIT_FAILURE;
}
/* Print diagnostics. */
if(diagnostics) {
const replayer_header *header;
const replayer_chunk_entry *chunks;
header = replayer_get_header(file);
if(header) {
if(verbose) printf("%s: Header read successfully\n", tool);
print_replay_header(stdout, header);
chunks = replayer_get_chunk_catalogue(file);
if(!chunks)
fprintf(stderr, "%s: Couldn't get chunk catalogue\n", tool);
else print_replay_chunk_catalogue(stdout, chunks,
header->no_chunks, header->no_soundtracks);
}
else { fprintf(stderr, "No good header\n"); }
}
/* Play the file, sound only. */
if(play) {
int rc, time_state = timer ? 0 : -1;
if(verbose) {
int len_cs = replayer_get_length(file);
char *len_str = len_cs > 0 ? time_string(len_cs) : 0;
printf("%s: Playing file %i, `%s' (length %s)\n", tool,
next_arg - first_file_arg, filename,
len_str ? len_str : "?");
free(len_str);
}
/* Set options. */
replayer_set_sound_quality(file, quality);
#ifndef replayer_NO_BACKWARDS
replayer_set_backwards(file, backwards);
#endif
/* Start playing. */
rc = replayer_sound_play(file, 0);
if(rc) {
fprintf(stderr, "%s: Playback failed (code %i)\n", tool, rc);
goto finish;
}
replayer_sound_set_mute(file, muted); /* Preserve mute state. */
while(1) {
int code;
#ifdef RISCOS
bool tw;
print_time(&time_state, file);
/* See if we're running in a TaskWindow. */
if(!xtaskwindowtaskinfo_window_task(&tw) && tw) {
/* The TaskWindow can let us sleep until a pollword becomes
non-zero. This is an under-used feature of the TaskWindow!
Hardly surprising when the documentation is in the wrong
section and is gibberish. */
const int *poll_word = replayer_get_poll_word(file);
if(poll_word) xupcall_sleep((int *) poll_word, 0);
/* This does work: It reduces time wasted in this loop and
improves desktop responsiveness. */
}
/* See if there is a keypress waiting. */
if(interactive) {
int key, key_unread;
if(!xos_byte(129, 0, 0, &key, &key_unread) && !key_unread) {
remove_time(&time_state);
switch(key) {
case 'p': case 'P': {
bool paused = !replayer_sound_get_pause(file);
if(replayer_sound_set_pause(file, paused))
printf("Couldn't pause\n");
else printf(paused ? "Paused\n" : "Unpaused\n");
break;
}
case 'm': case 'M': {
/* Mute state is preserved between files (pause is not). */
muted = !muted;
if(replayer_sound_set_mute(file, muted))
printf("Couldn't mute\n");
else printf(muted ? "Muted\n" : "Unmuted\n");
break;
}
case 'n': case 'N': goto finish;
case 'c': case 'C': {
char buffer[10], *c = buffer;
int ch, no;
printf("Change to nth file in list: ");
/* Read number from stdin. */
while(ch = getchar(), (ch != EOF && ch != '\n')) {
if(c >= (buffer + sizeof(buffer) - 1)) break;
*c++ = ch;
}
*c = 0;
no = atoi(buffer);
if(!no) break; /* Ignore if number was zero. */
next_arg = first_file_arg + no - 1;
goto finish;
}
case 'q': case 'Q': finish_loop = 1; goto finish;
case 'h': case 'H':
printf("\nAvailable keys are:\n\n"
" p Pause (and unpause) playback\n"
" m Mute (and unmute) playback\n"
" n End the current file (and start the next)\n"
" q End all files and quit\n"
" c Change file (prompts number)\n"
" h Show this message\n"
"\n");
break;
}
}
}
#else
print_time(&time_state, file);
#endif
code = replayer_sound_feed(file);
if(code > 0) {
remove_time(&time_state);
fprintf(stderr, "%s: Sound playback failed with error (%i)\n",
tool, code);
break;
}
if(code == replayer_feed_FINISHED) break;
}
remove_time(&time_state);
}
finish:
/* Destroy the Replay object and finish. */
replayer_destroy(file);
if(finish_loop) break;
if(loop && next_arg >= argc) next_arg = first_file_arg;
}
if(verbose) printf("%s: Finished\n", tool);
/* If you want, you can uncomment the next line to force MemCheck to
list blocks, and check for a memory leak */
#ifdef MemCheck_MEMCHECK
MemCheck_OutputBlocksInfo();
#endif
return EXIT_SUCCESS;
}
/* Prints the current time position. */
static void print_time(int *length, const replayer_file *file)
{
if(*length != -1) {
int time = replayer_sound_get_time(file);
if(*length) remove_time(length);
*length = printf(" %i:%c%c:%c%c ",
time / (60 * 100), /* Minutes */
(time / 1000) % 6 + '0', /* Seconds big digit */
(time / 100) % 10 + '0', /* Seconds small digit */
(time / 10) % 10 + '0', /* Centiseconds big digit */
time % 10 + '0'); /* Centiseconds small digit */
if(*length < 0) *length = 0;
}
}
/* Removes the previously-printed time. */
static void remove_time(int *length)
{
#ifdef RISCOS
if(*length > 0) {
while((*length)--) os_delete();
*length = 0;
}
#else
printf("\n");
#endif
}
/* Given a time in centiseconds, returns a string of the time in the form
`(MM)M:SS:CC'. Returns 0 for failure. Free the string yourself. */
static char *time_string(int time)
{
char *s = malloc(12);
if(!s) return 0;
sprintf(s, "%i:%c%c:%c%c",
time / (60 * 100), /* Minutes */
(time / 1000) % 6 + '0', /* Seconds big digit */
(time / 100) % 10 + '0', /* Seconds small digit */
(time / 10) % 10 + '0', /* Centiseconds big digit */
time % 10 + '0'); /* Centiseconds small digit */
return s;
}
/* Diagnostic functions */
/* This function prints the information in the Replay header to the
stream you give it. */
static void print_replay_header(FILE *file, const replayer_header *header)
{
int i;
fprintf(file, "\nARMovie header:\n");
fprintf(file, "\nMisc --\n");
fprintf(file, "Title: `%s'\n", header->title);
fprintf(file, "Date & (c): `%s'\n", header->date_and_copyright);
fprintf(file, "Other: `%s'\n", header->other_details);
fprintf(file, "\nVideo --\n");
fprintf(file, "Video type: `%s'\n", header->video_type);
fprintf(file, "Dimensions: %i x %i pixels\n",
header->width, header->height);
fprintf(file, "Colour depth: %i bpp\n", header->colour_depth);
fprintf(file, "Frames/sec: %i\n", header->fps);
fprintf(file, "\nSound --\n");
if(!header->no_soundtracks) fprintf(file, "(No soundtracks)\n");
for(i=0; i<header->no_soundtracks; ++i) {
char *driver;
fprintf(file, "%i.\n"
"Type: `%s'\n"
"Sample rate: %f Hz\n"
"Channels: %i\n"
"Precision: %i bits\n",
i + 1,
header->soundtracks[i].type,
header->soundtracks[i].rate,
header->soundtracks[i].channels,
header->soundtracks[i].precision);
fprintf(file, "Reverse channels (%i), linear (%i), unsigned (%i)\n",
header->soundtracks[i].reverse_channels,
header->soundtracks[i].linear_sound,
header->soundtracks[i].unsigned_sound);
driver = replayer_get_driver_filename(&header->soundtracks[i]);
if(driver) {
fprintf(file, "[Driver: `%s']\n", driver);
free(driver);
}
else { fprintf(file, "[Could not find driver]\n"); }
}
fprintf(file, "\nTiming and other --\n");
fprintf(file, "Chunks: %i\n", header->no_chunks);
fprintf(file, "Chunk catalogue: %i (offset)\n", header->chunk_catalogue);
fprintf(file, "Frames/chunk: %i\n", header->frames_per_chunk);
fprintf(file, "Even chunk size: %i\n", header->even_chunk_size);
fprintf(file, "Odd chunk size: %i\n", header->odd_chunk_size);
fprintf(file, "Sprite: %i (offset), %i (size)\n",
header->sprite_offset, header->sprite_size);
fprintf(file, "Key frames: %i (offset)\n", header->key_frames_offset);
}
/* Prints the Replay chunk catalogue given to the stream given.
You must pass the number of chunks (minus one, as given in the header)
and the number of soundtracks yourself, because these are given in the
header, not the catalogue itself. */
static void print_replay_chunk_catalogue(FILE *file,
const replayer_chunk_entry *chunks, int no_chunks, int soundtracks)
{
int i = 0, j;
const replayer_chunk_entry *c = chunks,
*end = (const replayer_chunk_entry *) ((const char *) chunks +
replayer_chunk_entry_SIZE(soundtracks) * (no_chunks + 1));
fprintf(file, "\nARMovie chunk catalogue:\n\n");
while(c < end) {
fprintf(file, "%i. "
"Offset: %i, "
"video size: %i",
i, c->offset, c->video_size);
for(j=0; j<soundtracks; ++j)
{ fprintf(file, ", sound#%i size: %i", j+1, c->sound_size[j]); }
fprintf(file, "\n");
++i;
c = (const replayer_chunk_entry *) ((const char *) c +
replayer_chunk_entry_SIZE(soundtracks));
}
}